home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / components / calCompositeCalendar.js < prev    next >
Encoding:
JavaScript  |  2007-09-22  |  19.7 KB  |  600 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. //
  40. // calCompositeCalendar.js
  41. //
  42.  
  43. const calIOperationListener = Components.interfaces.calIOperationListener;
  44.  
  45. function calCompositeCalendarObserverHelper (compCalendar) {
  46.     this.compCalendar = compCalendar;
  47.     this.pendingLoads = {};
  48. }
  49.  
  50. calCompositeCalendarObserverHelper.prototype = {
  51.     pendingLoads: null,
  52.  
  53.     onStartBatch: function() {
  54.         this.compCalendar.mObservers.notify("onStartBatch");
  55.     },
  56.  
  57.     onEndBatch: function() {
  58.         this.compCalendar.mObservers.notify("onEndBatch");
  59.     },
  60.  
  61.     onLoad: function(calendar) {
  62.         // avoid unnecessary onLoad events:
  63.         if (this.pendingLoads[calendar.id]) {
  64.             // don't forward if caused by composite:
  65.             delete this.pendingLoads[calendar.id];
  66.         } else {
  67.             // any refreshed dependent calendar logically refreshes
  68.             // this composite calendar, thus we send out an onLoad
  69.             // for this composite calendar:
  70.             this.compCalendar.mObservers.notify("onLoad", [this.compCalendar]);
  71.         }
  72.     },
  73.  
  74.     onAddItem: function(aItem) {
  75.         this.compCalendar.mObservers.notify("onAddItem", arguments);
  76.     },
  77.  
  78.     onModifyItem: function(aNewItem, aOldItem) {
  79.         this.compCalendar.mObservers.notify("onModifyItem", arguments);
  80.     },
  81.  
  82.     onDeleteItem: function(aDeletedItem) {
  83.         this.compCalendar.mObservers.notify("onDeleteItem", arguments);
  84.     },
  85.  
  86.     onError: function(aErrNo, aMessage) {
  87.         this.compCalendar.mObservers.notify("onError", arguments);
  88.     }
  89. };
  90.  
  91. function calCompositeCalendar () {
  92.     this.mObserverHelper = new calCompositeCalendarObserverHelper(this);
  93.     this.wrappedJSObject = this;
  94.  
  95.     this.mCalendars = new Array();
  96.     this.mCompositeObservers = new calListenerBag(Components.interfaces.calICompositeObserver);
  97.     this.mObservers = new calListenerBag(Components.interfaces.calIObserver);
  98.     this.mDefaultCalendar = null;
  99. }
  100.  
  101. calCompositeCalendar.prototype = {
  102.     //
  103.     // private members
  104.     //
  105.     mDefaultCalendar: null,
  106.  
  107.     //
  108.     // nsISupports interface
  109.     //
  110.     QueryInterface: function (aIID) {
  111.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  112.             !aIID.equals(Components.interfaces.calICalendarProvider) &&
  113.             !aIID.equals(Components.interfaces.calICalendar) &&
  114.             !aIID.equals(Components.interfaces.calICompositeCalendar))
  115.         {
  116.             throw Components.results.NS_ERROR_NO_INTERFACE;
  117.         }
  118.  
  119.         return this;
  120.     },
  121.  
  122.     //
  123.     // calICalendarProvider interface
  124.     //
  125.     get prefChromeOverlay() {
  126.         return null;
  127.     },
  128.  
  129.     get displayName() {
  130.         return calGetString("calendar", "compositeName");
  131.     },
  132.  
  133.     createCalendar: function comp_createCal() {
  134.         throw NS_ERROR_NOT_IMPLEMENTED;
  135.     },
  136.  
  137.     deleteCalendar: function comp_deleteCal(cal, listener) {
  138.         // You shouldn't be able to delete from the composite calendar.
  139.         throw NS_ERROR_NOT_IMPLEMENTED;
  140.     },
  141.  
  142.     //
  143.     // calICompositeCalendar interface
  144.     //
  145.  
  146.     mCalendars: null,
  147.     mDefaultCalendar: null,
  148.     mPrefPrefix: null,
  149.     mDefaultPref: null,
  150.     mActivePref: null,
  151.     
  152.     set prefPrefix (aPrefPrefix) {
  153.         if (this.mPrefPrefix) {
  154.             this.mCalendars.forEach(this.removeCalendar, this);
  155.         }
  156.         this.mPrefPrefix = aPrefPrefix;
  157.         this.mActivePref = aPrefPrefix + "-in-composite";
  158.         this.mDefaultPref = aPrefPrefix + "-default";
  159.         var mgr = getCalendarManager();
  160.         var cals = mgr.getCalendars({});
  161.  
  162.         cals.forEach(function (c) {
  163.             if (mgr.getCalendarPref(c, this.mActivePref))
  164.                 this.addCalendar(c);
  165.             if (mgr.getCalendarPref(c, this.mDefaultPref))
  166.                 this.setDefaultCalendar(c, false);
  167.         }, this);
  168.     },
  169.  
  170.     get prefPrefix () {
  171.         return this.mPrefPrefix;
  172.     },
  173.  
  174.     addCalendar: function (aCalendar) {
  175.         // check if the calendar already exists
  176.         for each (cal in this.mCalendars) {
  177.             if (aCalendar.uri.equals(cal.uri)) {
  178.                 // throw exception if calendar already exists?
  179.                 return;
  180.             }
  181.         }
  182.  
  183.         // add our observer helper
  184.         aCalendar.addObserver(this.mObserverHelper);
  185.  
  186.         this.mCalendars.push(aCalendar);
  187.         if (this.mPrefPrefix) {
  188.             getCalendarManager().setCalendarPref(aCalendar, this.mActivePref,
  189.                                          "true");
  190.         }
  191.         this.mCompositeObservers.notify("onCalendarAdded", [aCalendar]);
  192.  
  193.         // if we have no default calendar, we need one here
  194.         if (this.mDefaultCalendar == null)
  195.             this.setDefaultCalendar(aCalendar, false);
  196.     },
  197.  
  198.     removeCalendar: function (aServer) {
  199.         var newCalendars = Array();
  200.         var calToRemove = null;
  201.         for each (cal in this.mCalendars) {
  202.             if (!aServer.equals(cal.uri))
  203.                 newCalendars.push(cal);
  204.             else
  205.                 calToRemove = cal;
  206.         }
  207.  
  208.         if (calToRemove) {
  209.             this.mCalendars = newCalendars;
  210.             if (this.mPrefPrefix) {
  211.                 getCalendarManager().deleteCalendarPref(calToRemove,
  212.                                                 this.mActivePref);
  213.                 getCalendarManager().deleteCalendarPref(calToRemove,
  214.                                                 this.mDefaultPref);
  215.             }   
  216.             calToRemove.removeObserver(this.mObserverHelper);
  217.             this.mCompositeObservers.notify("onCalendarRemoved", [calToRemove]);
  218.         }
  219.     },
  220.  
  221.     getCalendar: function (aServer) {
  222.         for each (cal in this.mCalendars) {
  223.             if (aServer.equals(cal.uri))
  224.                 return cal;
  225.         }
  226.  
  227.         return null;
  228.     },
  229.  
  230.     get calendars() {
  231.         // return a nsISimpleEnumerator of this array.  This sucks.
  232.         // XXX make this an array, like the calendar manager?
  233.         return null;
  234.     },
  235.  
  236.     get defaultCalendar() { 
  237.         return this.mDefaultCalendar;
  238.     },
  239.  
  240.     setDefaultCalendar: function (cal, usePref) {
  241.         // don't do anything if the passed calendar is the default calendar!
  242.         if (this.mDefaultCalendar && cal && this.mDefaultCalendar.uri.equals(cal.uri))
  243.             return;
  244.         if (usePref && this.mPrefPrefix) {
  245.             if (this.mDefaultCalendar) {
  246.                 getCalendarManager().deleteCalendarPref(this.mDefaultCalendar,
  247.                                                 this.mDefaultPref);
  248.             }
  249.             // if not null set the new calendar as default in the preferences
  250.             if (cal)  {
  251.                 getCalendarManager().setCalendarPref(cal, this.mDefaultPref,
  252.                                                      "true");
  253.             }
  254.         }
  255.         this.mDefaultCalendar = cal;
  256.         this.mCompositeObservers.notify("onDefaultCalendarChanged", [cal]);
  257.     },
  258.  
  259.     set defaultCalendar(v) {
  260.         this.setDefaultCalendar(v, true);
  261.     },
  262.  
  263.     //
  264.     // calICalendar interface
  265.     //
  266.     // Write operations here are forwarded to either the item's
  267.     // parent calendar, or to the default calendar if one is set.
  268.     // Get operations are sent to each calendar.
  269.     //
  270.  
  271.     get id() {
  272.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  273.     },
  274.     set id(id) {
  275.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  276.     },
  277.  
  278.     // this could, at some point, return some kind of URI identifying
  279.     // all the child calendars, thus letting us create nifty calendar
  280.     // trees.
  281.     get uri() {
  282.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  283.     },
  284.     set uri(v) {
  285.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  286.     },
  287.  
  288.     get readOnly() { 
  289.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  290.     },
  291.     set readOnly(bool) {
  292.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  293.     },
  294.  
  295.     get canRefresh() {
  296.         return true;
  297.     },
  298.  
  299.     // void addObserver( in calIObserver observer );
  300.     mCompositeObservers: null,
  301.     mObservers: null,
  302.     addObserver: function (aObserver) {
  303.         if (aObserver instanceof Components.interfaces.calICompositeObserver) {
  304.             this.mCompositeObservers.add(aObserver);
  305.         }
  306.         this.mObservers.add(aObserver);
  307.     },
  308.  
  309.     // void removeObserver( in calIObserver observer );
  310.     removeObserver: function (aObserver) {
  311.         if (aObserver instanceof Components.interfaces.calICompositeObserver) {
  312.             this.mCompositeObservers.remove(aObserver);
  313.         }
  314.         this.mObservers.remove(aObserver);
  315.     },
  316.  
  317.     refresh: function() {
  318.         for each (cal in this.mCalendars) {
  319.             try {
  320.                 if (cal.canRefresh) {
  321.                     this.mObserverHelper.pendingLoads[cal.id] = true;
  322.                     cal.refresh();
  323.                 }
  324.             } catch (e) {
  325.                 ASSERT(false, e);
  326.                 delete this.mObserverHelper.pendingLoads[cal.id];
  327.             }
  328.         }
  329.         // send out a single onLoad for this composite calendar,
  330.         // although e.g. the ics provider will trigger another
  331.         // onLoad asynchronously; we cannot rely on every calendar
  332.         // sending an onLoad:
  333.         this.mObservers.notify("onLoad", [this]);
  334.     },
  335.  
  336.     // void modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem, in calIOperationListener aListener );
  337.     modifyItem: function (aNewItem, aOldItem, aListener) {
  338.         if (aNewItem.calendar == null) {
  339.             // XXX Can't modify item with NULL parent
  340.             throw Components.results.NS_ERROR_FAILURE;
  341.         }
  342.  
  343.         return aNewItem.calendar.modifyItem(aNewItem, aOldItem, aListener);
  344.     },
  345.  
  346.     // void deleteItem( in string id, in calIOperationListener aListener );
  347.     deleteItem: function (aItem, aListener) {
  348.         if (aItem.calendar == null) {
  349.             // XXX Can't delete item with NULL parent
  350.             throw Components.results.NS_ERROR_FAILURE;
  351.         }
  352.  
  353.         return aItem.calendar.deleteItem(aItem, aListener);
  354.     },
  355.  
  356.     // void addItem( in calIItemBase aItem, in calIOperationListener aListener );
  357.     addItem: function (aItem, aListener) {
  358.         return this.mDefaultCalendar.addItem(aItem, aListener);
  359.     },
  360.  
  361.     // void getItem( in string aId, in calIOperationListener aListener );
  362.     getItem: function (aId, aListener) {
  363.         var cmpListener = new calCompositeGetListenerHelper(this.mCalendars.length, aListener);
  364.         for each (cal in this.mCalendars) {
  365.             try {
  366.                 cmpListener.opGroup.add(cal.getItem(aId, cmpListener));
  367.             } catch (exc) {
  368.                 ASSERT(false, exc);
  369.             }
  370.         }
  371.         return cmpListener.opGroup;
  372.     },
  373.  
  374.     // void getItems( in unsigned long aItemFilter, in unsigned long aCount, 
  375.     //                in calIDateTime aRangeStart, in calIDateTime aRangeEnd,
  376.     //                in calIOperationListener aListener );
  377.     getItems: function (aItemFilter, aCount, aRangeStart, aRangeEnd, aListener) {
  378.         // If there are no calendars, then we just call onOperationComplete
  379.         if (this.mCalendars.length == 0) {
  380.             aListener.onOperationComplete (this,
  381.                                            Components.results.NS_OK,
  382.                                            calIOperationListener.GET,
  383.                                            null,
  384.                                            null);
  385.             return;
  386.         }
  387.  
  388.         var cmpListener = new calCompositeGetListenerHelper(this.mCalendars.length, aListener, aCount);
  389.         for (cal in this.mCalendars) {
  390.             try {
  391.                 cmpListener.opGroup.add(
  392.                     this.mCalendars[cal].getItems(
  393.                         aItemFilter, aCount, aRangeStart, aRangeEnd, cmpListener));
  394.             } catch (exc) {
  395.                 ASSERT(false, exc);
  396.             }
  397.         }
  398.         return cmpListener.opGroup;
  399.     },
  400.  
  401.     startBatch: function ()
  402.     {
  403.         this.mCompositeObservers.notify("onStartBatch");
  404.     },
  405.     endBatch: function ()
  406.     {
  407.         this.mCompositeObservers.notify("onEndBatch");
  408.     }
  409. };
  410.  
  411. // composite listener helper
  412. function calCompositeGetListenerHelper(aNumQueries, aRealListener, aMaxItems) {
  413.     this.wrappedJSObject = this;
  414.     this.mNumQueries = aNumQueries;
  415.     this.mRealListener = aRealListener;
  416.     this.mMaxItems = aMaxItems;
  417. }
  418.  
  419. calCompositeGetListenerHelper.prototype = {
  420.     mNumQueries: 0,
  421.     mRealListener: null,
  422.     mOpGroup: null,
  423.     mReceivedCompletes: 0,
  424.     mFinished: false,
  425.     mMaxItems: 0,
  426.     mItemsReceived: 0,
  427.  
  428.     get opGroup() {
  429.         if (!this.mOpGroup) {
  430.             var this_ = this;
  431.             function cancelFunc() { // operation group has been cancelled
  432.                 var listener = this_.mRealListener;
  433.                 this_.mRealListener = null;
  434.                 if (listener) {
  435.                     listener.onOperationComplete(
  436.                         this_, Components.interfaces.calIErrors.OPERATION_CANCELLED,
  437.                         calIOperationListener.GET, null, null);
  438.                 }
  439.             }
  440.             this.mOpGroup = new calOperationGroup(cancelFunc);
  441.         }
  442.         return this.mOpGroup;
  443.     },
  444.  
  445.     QueryInterface: function (aIID) {
  446.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  447.             !aIID.equals(Components.interfaces.calIOperationListener))
  448.         {
  449.             throw Components.results.NS_ERROR_NO_INTERFACE;
  450.         }
  451.  
  452.         return this;
  453.     },
  454.  
  455.     onOperationComplete: function (aCalendar, aStatus, aOperationType, aId, aDetail) {
  456.         if (!this.mRealListener) {
  457.             // has been cancelled, ignore any providers firing on this...
  458.             return;
  459.         }
  460.         if (this.mFinished) {
  461.             dump ("+++ calCompositeGetListenerHelper.onOperationComplete: called with mFinished == true!");
  462.             return;
  463.         }
  464.  
  465.         if (!Components.isSuccessCode(aStatus)) {
  466.             // proxy this to a onGetResult
  467.             // XXX - do we want to give the real calendar? or this?
  468.             // XXX - get rid of iid param
  469.             this.mRealListener.onGetResult (aCalendar, aStatus, 
  470.                                             Components.interfaces.nsISupports,
  471.                                             aDetail, 0, []);
  472.         }
  473.  
  474.         this.mReceivedCompletes++;
  475.  
  476.         if (this.mReceivedCompletes == this.mNumQueries) {
  477.             // we're done here.
  478.             this.mRealListener.onOperationComplete (this,
  479.                                                     aStatus,
  480.                                                     calIOperationListener.GET,
  481.                                                     null,
  482.                                                     null);
  483.             this.mFinished = true;
  484.             this.opGroup.notifyCompleted();
  485.         }
  486.     },
  487.  
  488.     onGetResult: function (aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  489.         if (!this.mRealListener) {
  490.             // has been cancelled, ignore any providers firing on this...
  491.             return;
  492.         }
  493.         if (this.mFinished) {
  494.             dump ("+++ calCompositeGetListenerHelper.onGetResult: called with mFinished == true!");
  495.             return;
  496.         }
  497.  
  498.         // ignore if we have a max and we're past it
  499.         if (this.mMaxItems && this.mItemsReceived >= this.mMaxItems)
  500.             return;
  501.  
  502.         if (Components.isSuccessCode(aStatus) &&
  503.             this.mMaxItems &&
  504.             ((this.mItemsReceived + aCount) > this.mMaxItems))
  505.         {
  506.             // this will blow past the limit
  507.             aCount = this.mMaxItems - this.mItemsReceived;
  508.             aItems = aItems.slice(0, numToSend);
  509.         }
  510.  
  511.         // send GetResults to the real listener
  512.         this.mRealListener.onGetResult (aCalendar, aStatus, aItemType, aDetail, aCount, aItems);
  513.         this.mItemsReceived += aCount;
  514.     }
  515.  
  516. };
  517.  
  518.  
  519.  
  520. /****
  521.  **** module registration
  522.  ****/
  523.  
  524. var calCompositeCalendarModule = {
  525.     mCID: Components.ID("{aeff788d-63b0-4996-91fb-40a7654c6224}"),
  526.     mContractID: "@mozilla.org/calendar/calendar;1?type=composite",
  527.  
  528.     mUtilsLoaded: false,
  529.     loadUtils: function compositeLoadUtils() {
  530.         if (this.mUtilsLoaded)
  531.             return;
  532.  
  533.         const jssslContractID = "@mozilla.org/moz/jssubscript-loader;1";
  534.         const jssslIID = Components.interfaces.mozIJSSubScriptLoader;
  535.  
  536.         const iosvcContractID = "@mozilla.org/network/io-service;1";
  537.         const iosvcIID = Components.interfaces.nsIIOService;
  538.  
  539.         var loader = Components.classes[jssslContractID].getService(jssslIID);
  540.         var iosvc = Components.classes[iosvcContractID].getService(iosvcIID);
  541.  
  542.         // Note that unintuitively, __LOCATION__.parent == .
  543.         // We expect to find utils in ./../js
  544.         var appdir = __LOCATION__.parent.parent;
  545.         appdir.append("js");
  546.         var scriptName = "calUtils.js";
  547.  
  548.         var f = appdir.clone();
  549.         f.append(scriptName);
  550.  
  551.         try {
  552.             var fileurl = iosvc.newFileURI(f);
  553.             loader.loadSubScript(fileurl.spec, this.__parent__.__parent__);
  554.         } catch (e) {
  555.             dump("Error while loading " + fileurl.spec + "\n");
  556.             throw e;
  557.         }
  558.  
  559.         this.mUtilsLoaded = true;
  560.     },
  561.     
  562.     registerSelf: function (compMgr, fileSpec, location, type) {
  563.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  564.         compMgr.registerFactoryLocation(this.mCID,
  565.                                         "Calendar composite provider",
  566.                                         this.mContractID,
  567.                                         fileSpec,
  568.                                         location,
  569.                                         type);
  570.     },
  571.  
  572.     getClassObject: function (compMgr, cid, iid) {
  573.         if (!cid.equals(this.mCID))
  574.             throw Components.results.NS_ERROR_NO_INTERFACE;
  575.  
  576.         if (!iid.equals(Components.interfaces.nsIFactory))
  577.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  578.  
  579.         this.loadUtils();
  580.  
  581.         return this.mFactory;
  582.     },
  583.  
  584.     mFactory: {
  585.         createInstance: function (outer, iid) {
  586.             if (outer != null)
  587.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  588.             return (new calCompositeCalendar()).QueryInterface(iid);
  589.         }
  590.     },
  591.  
  592.     canUnload: function(compMgr) {
  593.         return true;
  594.     }
  595. };
  596.  
  597. function NSGetModule(compMgr, fileSpec) {
  598.     return calCompositeCalendarModule;
  599. }
  600.